package net.tavion.angelboot.fegin.interceptor;

import feign.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.cloud.openfeign.FeignClient;

import java.lang.reflect.Method;

public class FeignClientPointcut extends StaticMethodMatcherPointcut {
    private static final Logger logger = LoggerFactory.getLogger(FeignClientPointcut.class);

    public FeignClientPointcut() {
    }

    public boolean matches(Method method, Class<?> targetClass) {
        boolean matches = this.matchesFeignClient(targetClass);
        if (matches) {
            matches = this.matchesClientMethod(method);
        }

        if (matches && logger.isInfoEnabled()) {
            logger.info("Intercepted feign client {}, class name is {}.", targetClass.getInterfaces()[0], targetClass);
        }

        return matches;
    }

    private boolean matchesFeignClient(Class<?> targetClass) {
        Class<?>[] interfaces = targetClass.getInterfaces();
        if (interfaces == null | interfaces.length != 1) {
            return false;
        } else {
            boolean b = interfaces[0].isAnnotationPresent(FeignClient.class);
            return b;
        }
    }

    private boolean matchesClientMethod(Method method) {
        return method.getDeclaringClass() != Object.class && (method.getModifiers() & 8) == 0 && !Util.isDefault(method);
    }

    static class ClientKey {
        private Class<?> clazz;
        private Method method;

        public ClientKey(Class<?> clazz, Method method) {
            this.clazz = clazz;
            this.method = method;
        }

        public boolean equals(Object o) {
            if (this == o) {
                return true;
            } else if (o != null && this.getClass() == o.getClass()) {
                FeignClientPointcut.ClientKey clientKey = (FeignClientPointcut.ClientKey)o;
                if (this.clazz != null) {
                    if (this.clazz.equals(clientKey.clazz)) {
                        return this.method != null ? this.method.equals(clientKey.method) : clientKey.method == null;
                    }
                } else if (clientKey.clazz == null) {
                    return this.method != null ? this.method.equals(clientKey.method) : clientKey.method == null;
                }

                return false;
            } else {
                return false;
            }
        }

        public int hashCode() {
            int result = this.clazz != null ? this.clazz.hashCode() : 0;
            result = 31 * result + (this.method != null ? this.method.hashCode() : 0);
            return result;
        }
    }
}